#define _CRT_SECURE_NO_WARNINGS

#include <cstring>
#include <map>

#include "file_op.h"
#include "str_op.h"

#include "config.h"

//Concentration iterator variables.
double c_start, c_end, c_step;
double s_start, s_end, s_step;
bool c_log, s_log;

//Dump data switches.
bool dump_ne_nf, dump_nef, dump_ec;
bool dump_single_ne_nf, dump_single_nef, dump_single_ec;

//EC export variables.
std::vector<double> ec_list;
double ec_epsinon;
char output_dir[256];
char pki_file_path[256];
char network_file_path[256];

// Based on operating system, define the filter.
#ifdef __unix__
static const char *path_splitter = "/";
#else
static const char *path_splitter = "\\";
#endif

double parse_double(const std::map<std::string, std::string> &cfg_data,
                    const std::string &name)
{
    if(cfg_data.find(name) == cfg_data.end())
    {
        //Not found the data.
        return 0.0;
    }
    double result;
    sscanf(cfg_data.at(name).c_str(), "%lf", &result);
    return result;
}

bool parse_bool(const std::map<std::string, std::string> &cfg_data,
                const std::string &name)
{
    if(cfg_data.find(name) == cfg_data.end())
    {
        //Not found the data.
        return false;
    }
    std::string sl = cfg_data.at(name);
    transform(sl.begin(), sl.end(), sl.begin(), ::tolower);
    return sl == "true";
}

std::vector<int> parse_int_list(const std::map<std::string, std::string> &cfg_data,
                                const std::string &name)
{
    //Prepare an empty list first.
    std::vector<int> list;
    if(cfg_data.find(name) == cfg_data.end())
    {
        //Not found the data.
        return list;
    }
    const std::string &sl = cfg_data.at(name);
    //Loop and find all the comma.
    const char *buf = sl.c_str();
    int lastPos = -1, nextComma = indexOf(',', buf, sl.size(), 0);
    // Loop while all the comma is find.
    int element;
    while(nextComma != -1)
    {
        //Read the element.
        sscanf(sl.c_str() + lastPos + 1, "%d", &element);
        //Add the data to list
        list.push_back(element);
        //Find the next comma.
        nextComma = indexOf(',', buf, sl.size(), lastPos + 1);
        lastPos = nextComma;
    }
    return list;
}

void parse_str(const std::map<std::string, std::string> &cfg_data,
               const std::string &name,
               char *buffer)
{
    //Copy the data to buffer.
    strcpy(buffer, cfg_data.at(name).c_str());
}

void load_config(const char *config_path)
{
    //Read the configuration file.
    FILE *config_file = open_file_read(config_path,
                                       "cannot open configure file.");
    //Create configure buffer.
    std::map<std::string, std::string> config_data;
    //Create line reading buffer.
    char buffer[LINE_BUFFER_SIZE];
    while(fgets(buffer, LINE_BUFFER_SIZE, config_file) != NULL)
    {
        //Find the first equal mark.
        int equal_mark = indexOf('=', buffer, strlen(buffer));
        if(equal_mark==-1)
        {
            continue;
        }
        //Parse the name and value.
        std::string name = std::string(buffer, equal_mark);
        std::string value = std::string(buffer + equal_mark + 1);
        trim(name);
        trim(value);
        //Add the name and value to map.
        config_data.insert(std::pair<std::string, std::string>(name, value));
    }
    fclose(config_file);
    //Based on the map, parse the data.
    c_start = parse_double(config_data, "axis_start");
    c_end = parse_double(config_data, "axis_end");
    c_step = parse_double(config_data, "axis_step");
    c_log = parse_bool(config_data, "axis_log");
    s_start = parse_double(config_data, "single_start");
    s_end = parse_double(config_data, "single_end");
    s_step = parse_double(config_data, "single_step");
    s_log = parse_bool(config_data, "single_log");
    dump_ne_nf = parse_bool(config_data, "dump_ne_nf");
    dump_nef = parse_bool(config_data, "dump_nef");
    dump_ec = parse_bool(config_data, "dump_ec");
    dump_single_ne_nf = parse_bool(config_data, "dump_single_ne_nf");
    dump_single_nef = parse_bool(config_data, "dump_single_nef");
    dump_single_ec = parse_bool(config_data, "dump_single_ec");
    std::vector<int> ec_raw_list = parse_int_list(config_data, "ec");
    ec_list.reserve(ec_raw_list.size());
    for(auto ec_raw : ec_raw_list)
    {
        ec_list.push_back(ec_raw / 100.0);
    }
    ec_epsinon = parse_double(config_data, "ec_bias");
    //Load the file path.
    parse_str(config_data, "output_dir", output_dir);
    parse_str(config_data, "pki_path", pki_file_path);
    parse_str(config_data, "network_path", network_file_path);
    //Check the last character of the output directory path.
    if(output_dir[strlen(output_dir)-1] != path_splitter[0])
    {
        //Append the splitter to output dir string.
        strcat(output_dir, path_splitter);
    }
}

void generate_output_path(const char *filename, char *filepath)
{
    //Copy the output file path.
    strcpy(filepath, output_dir);
    //Concatenate the path with the file name.
    strcat(filepath, filename);
}
